package com.allwees.bs.c.module.security;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author Daniel
 * @version 1.0.0
 * @since 2020/11/26 13:59
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Resource
    private ApplicationContext applicationContext;

    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Resource
    private AccessDeniedHandler accessDeniedHandler;

    @Resource
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    @Resource
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Resource
    private JwtTokenHelper jwtTokenHelper;

    @Resource
    private JwtTokenConfig jwtTokenConfig;

    @Value("${project.language:en}")
    private String language;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity htpp) throws Exception {
        htpp.cors().and().csrf().disable()
                //.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler)
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().headers().cacheControl().disable().frameOptions().disable()

                .and().formLogin()
                .loginProcessingUrl("/oauth/token")
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler)
                //Ensures the urls for failureUrl(String) as well as for the HttpSecurityBuilder,
                //the getLoginPage and getLoginProcessingUrl are granted access to any user.
                .permitAll()
                .and().authorizeRequests()
                .antMatchers(anonymousUrls()).permitAll()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .anyRequest().authenticated();

//        htpp.addFilterAfter(new I18nFilter(new Locale(language)), LogoutFilter.class);
        htpp.addFilterBefore(new JwtTokenRequestFilter(jwtTokenHelper, jwtTokenConfig), UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/", "/*.html", "/*.js", "/*.css", "/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security"
                , "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/csrf", "/error")
                .antMatchers(ignoreUrls());
    }

    private String[] anonymousUrls() {
        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
        Set<String> anonymousUrls = new HashSet<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> methodEntry : handlerMethodMap.entrySet()) {
            HandlerMethod handlerMethod = methodEntry.getValue();
            AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
            if (null != anonymousAccess) {
                anonymousUrls.addAll(methodEntry.getKey().getPatternsCondition().getPatterns());
            }
        }
        return anonymousUrls.toArray(new String[0]);
    }

    private String[] ignoreUrls() {
        return new String[]{
                "/api/test/**",
                "/api/v1/share/**",
                "/api/v1/payment/paypal/return",
                "/api/v1/payment/paypal/cancel",
                "/api/v1/payment/app/paypal/return",
                "/api/v1/payment/app/paypal/cancel",
                "/api/v1/user/forget-password",
                "/api/v1/user/thirdPart/login",
                "/api/v1/user/reset-password",
                "/api/home/mix",
                "/api/v1/ads/**",
                "/api/v2/upload/**",
                "/api/v1/product/review/**",
                "/api/v1/product/review/**",
                "/api/v1/marketing/flash",
                "/api/v1/marketing/gifts",
                "/api/v1/marketing/influs",
                "/api/v1/marketing/below5",
                "/api/v1/countries",
                "/api/v1/orders/**",
                "/api/v1/app/**",
                "/api/v2/app/**",
                "/api/v1/feedback",
                "/api/v1/user/register",
                "/api/v1/user/visitorShippingAddress",
                "/api/webhook/process",
                "/api/webhook/oceanpay",
                "/api/webhook/payby/payResult",
                "/api/webhook/payby/refundResult",
                "/api/country/resolve",
                "/api/v1/edm/**",
                "/druid/**",
                "/api/address/**"
        };
    }
}
